/*
 * Copyright 2015 泛泛o0之辈
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package cn.jfast.framework.schedule;

import cn.jfast.framework.base.util.Assert;
import cn.jfast.framework.base.cache.Cache;
import cn.jfast.framework.log.LogFactory;
import cn.jfast.framework.log.LogType;
import cn.jfast.framework.log.Logger;
import cn.jfast.framework.schedule.analyzer.CronAnalyzer;

import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;

public class ScheduleExecuter {
	
	private Logger log = LogFactory.getLogger(LogType.JFast, ScheduleExecuter.class);
    private static ScheduledExecutorService service;
    private static ScheduleExecuter me;
    private Cache jobs;
    private boolean isInit = false;

    private ScheduleExecuter() {

    }

    public static ScheduleExecuter me() {
        if (null == me) {
            me = new ScheduleExecuter();
        }
        return me;
    }

    public ScheduleExecuter initService(Cache jobs) {
        this.jobs = jobs;
        service = Executors.newScheduledThreadPool(jobs.size());
        isInit = true;
        return this;
    }

    public void start() {
        Assert.isTrue(isInit, "定时任务未初始化");
        for (final Object obj : jobs.getNativeCache().values()) {
            final ScheduledJob job = (ScheduledJob) obj;
            String jobName = job.getSchedule().getClass().getName() + "." + job.getMethod().getName() + "()";
            final String cron = job.getCron();
            final long delay = job.getDelay();
            final long repeatInterval = job.getRepeatInterval();
            final int repeat = job.getRepeat();
            if (cron.equals("")) {
                if (repeat >= 0) {
                    service.schedule(new Runnable() {
                        public void run() {
                            try {
                                job.getMethod().invoke(job.getSchedule());
                            } catch (Exception e) {
                                log.error("", e);
                            }
                            iteratorJob(job, cron, delay, repeat - 1, repeatInterval);
                        }

                    }, delay, TimeUnit.MILLISECONDS);
                }
            } else {
                service.schedule(new Runnable() {
                    public void run() {
                        try {
                            job.getMethod().invoke(job.getSchedule());
                        } catch (Exception e) {
                        	log.error("", e);
                        }
                        iteratorJob(job, cron, delay, repeat - 1, repeatInterval);
                    }

                }, new CronAnalyzer(cron, jobName).getExecuteTime(), TimeUnit.MILLISECONDS);
            }
        }
    }

    private void iteratorJob(final ScheduledJob job, final String cron,
                             final long delay, final int repeat, final long repeatInterval) {
        String jobName = job.getSchedule().getClass().getName() + "." + job.getMethod().getName() + "()";
        if (cron.equals("")) {
            if (repeat > 0) {
                service.schedule(new Runnable() {
                    public void run() {
                        try {
                            job.getMethod().invoke(job.getSchedule());
                        } catch (Exception e) {
                        	log.error("", e);
                        }
                        iteratorJob(job, cron, delay, repeat - 1, repeatInterval);
                    }

                }, repeatInterval, TimeUnit.MILLISECONDS);
            }
        } else {
            service.schedule(new Runnable() {
                public void run() {
                    try {
                        job.getMethod().invoke(job.getSchedule());
                    } catch (Exception e) {
                    	log.error("", e);
                    }
                    iteratorJob(job, cron, delay, repeat - 1, repeatInterval);
                }

            }, new CronAnalyzer(cron, jobName).getExecuteTime(), TimeUnit.MILLISECONDS);
        }
    }

    public void stop() {
        service.shutdownNow();
    }
}
